"""
数据获取器 - 从币安API获取虚拟货币数据
"""

import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from binance.client import Client
from binance.exceptions import BinanceAPIException
import pandas as pd
from datetime import datetime, timedelta
import logging
import time
from typing import Optional, List


logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class CryptoDataFetcher:
    """虚拟货币数据获取器"""
    
    def __init__(self, api_key: Optional[str] = None, secret_key: Optional[str] = None):
        """
        初始化数据获取器
        
        Args:
            api_key: 币安API密钥（可选，用于私有端点）
            secret_key: 币安API秘钥（可选，用于私有端点）
        """
        try:
            if api_key and secret_key:
                self.client = Client(api_key, secret_key)
                self.client.ping()
                logger.info("币安API连接成功（已认证）")
            else:
                self.client = Client()
                logger.info("使用币安公共API端点")
        except Exception as e:
            logger.error(f"币安API连接失败: {e}")
            self.client = Client()
            logger.info("回退到公共API端点")
    
    def get_available_symbols(self, quote_asset: str = 'USDT') -> List[str]:
        """
        获取可用的交易对列表
        
        Args:
            quote_asset: 计价货币，默认USDT
            
        Returns:
            交易对符号列表
        """
        try:
            exchange_info = self.client.get_exchange_info()
            symbols = []
            
            for symbol_info in exchange_info['symbols']:
                if (symbol_info['status'] == 'TRADING' and 
                    symbol_info['quoteAsset'] == quote_asset):
                    symbols.append(symbol_info['symbol'])
            
            logger.info(f"找到 {len(symbols)} 个 {quote_asset} 交易对")
            return sorted(symbols)
            
        except BinanceAPIException as e:
            logger.error(f"获取交易对信息失败: {e}")
            return []
    
    def get_quote_base_map(self, quote_asset: str = 'USDT') -> dict:
        """
        获取指定计价货币下的所有交易对，返回 baseAsset -> symbol 的映射
        
        Args:
            quote_asset: 计价货币，默认USDT
        
        Returns:
            字典映射，例如 { 'BTC': 'BTCUSDT', 'ETH': 'ETHUSDT', ... }
        """
        try:
            exchange_info = self.client.get_exchange_info()
            base_to_symbol = {}
            for symbol_info in exchange_info['symbols']:
                if (
                    symbol_info.get('status') == 'TRADING' and 
                    symbol_info.get('quoteAsset') == quote_asset
                ):
                    base_asset = symbol_info.get('baseAsset', '').upper()
                    symbol = symbol_info.get('symbol')
                    if base_asset and symbol:
                        base_to_symbol[base_asset] = symbol
            return base_to_symbol
        except BinanceAPIException as e:
            logger.error(f"获取交易对映射失败: {e}")
            return {}
    
    def get_historical_data(self, 
                          symbol: str, 
                          interval: str = '1h', 
                          days: int = 30) -> pd.DataFrame:
        """
        获取历史K线数据
        
        Args:
            symbol: 交易对符号（如'BTCUSDT'）
            interval: K线间隔（1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M）
            days: 获取的天数
            
        Returns:
            包含OHLCV数据的DataFrame
        """
        try:
            # 计算需要获取的数据点数
            interval_minutes = self._interval_to_minutes(interval)
            limit = min(1000, (days * 24 * 60) // interval_minutes)
            
            logger.info(f"获取 {symbol} 最近 {days} 天的 {interval} K线数据...")
            
            klines = self.client.get_klines(
                symbol=symbol,
                interval=interval,
                limit=limit
            )
            
            if not klines:
                logger.warning(f"未获取到 {symbol} 的数据")
                return pd.DataFrame()
            
            df = pd.DataFrame(klines, columns=[
                'timestamp', 'open', 'high', 'low', 'close', 'volume',
                'close_time', 'quote_asset_volume', 'number_of_trades',
                'taker_buy_base_asset_volume', 'taker_buy_quote_asset_volume', 'ignore'
            ])
            
            # 数据类型转换
            df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
            numeric_columns = ['open', 'high', 'low', 'close', 'volume', 'quote_asset_volume']
            for col in numeric_columns:
                df[col] = pd.to_numeric(df[col])
            
            df.set_index('timestamp', inplace=True)
            
            # 只保留需要的列
            df = df[['open', 'high', 'low', 'close', 'volume']]
            
            logger.info(f"成功获取 {len(df)} 条 {symbol} 数据")
            return df
            
        except BinanceAPIException as e:
            logger.error(f"获取 {symbol} 历史数据失败: {e}")
            return pd.DataFrame()
        except Exception as e:
            logger.error(f"处理 {symbol} 数据时出错: {e}")
            return pd.DataFrame()
    
    def _interval_to_minutes(self, interval: str) -> int:
        """
        将间隔字符串转换为分钟数
        
        Args:
            interval: 时间间隔字符串
            
        Returns:
            分钟数
        """
        interval_map = {
            '1m': 1, '3m': 3, '5m': 5, '15m': 15, '30m': 30,
            '1h': 60, '2h': 120, '4h': 240, '6h': 360, '8h': 480, '12h': 720,
            '1d': 1440, '3d': 4320, '1w': 10080, '1M': 43200
        }
        return interval_map.get(interval, 60)

    def fetch_volumes_24h(self, quote_asset: str = 'USDT') -> dict:
        """
        获取各交易对24小时成交额（USDT计价）。
        
        Returns:
            { 'BTC': quoteVolume_float, ... }
        """
        try:
            tickers = self.client.get_ticker()
            base_to_volume = {}
            for t in tickers:
                symbol = t.get('symbol', '')
                if not symbol.endswith(quote_asset):
                    continue
                try:
                    quote_volume = float(t.get('quoteVolume', 0.0))
                except Exception:
                    quote_volume = 0.0
                base_asset = symbol.replace(quote_asset, '')
                base_to_volume[base_asset] = quote_volume
            return base_to_volume
        except Exception as e:
            logger.error(f"获取24小时成交额失败: {e}")
            return {}

    def get_batch_historical_data(self, 
                                 symbols: List[str], 
                                 interval: str = '1h', 
                                 days: int = 30) -> dict:
        """
        批量获取多个交易对的历史K线数据，优化API调用次数
        
        Args:
            symbols: 交易对符号列表
            interval: K线间隔
            days: 获取的天数
            
        Returns:
            字典格式: {symbol: DataFrame, ...}
        """
        results = {}
        
        logger.info(f"开始批量获取{len(symbols)}个交易对的历史数据...")
        
        for i, symbol in enumerate(symbols, 1):
            try:
                logger.info(f"[{i}/{len(symbols)}] 获取 {symbol} 数据...")
                
                data = self.get_historical_data(symbol, interval, days)
                
                if not data.empty:
                    results[symbol] = data
                    logger.info(f"[OK] {symbol}: 成功获取 {len(data)} 条数据")
                else:
                    logger.warning(f"[WARN] {symbol}: 未获取到数据")
                
                # API限流：每个请求间隔0.1秒，避免触发限制
                time.sleep(0.1)
                
            except Exception as e:
                logger.error(f"[ERROR] {symbol}: 获取数据失败 - {e}")
                continue
        
        logger.info(f"批量数据获取完成: 成功 {len(results)}/{len(symbols)} 个交易对")
        return results
    
    def get_popular_symbols(self, count: int = 20) -> List[str]:
        """
        获取热门交易对列表
        
        Args:
            count: 返回的数量
            
        Returns:
            热门交易对列表
        """
        popular_symbols = [
            'BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'XRPUSDT', 'ADAUSDT',
            'SOLUSDT', 'DOGEUSDT', 'DOTUSDT', 'MATICUSDT', 'LTCUSDT',
            'AVAXUSDT', 'UNIUSDT', 'LINKUSDT', 'ATOMUSDT', 'ETCUSDT',
            'XLMUSDT', 'BCHUSDT', 'FILUSDT', 'TRXUSDT', 'EOSUSDT'
        ]
        
        # 验证交易对是否存在
        available_symbols = self.get_available_symbols()
        valid_symbols = [symbol for symbol in popular_symbols if symbol in available_symbols]
        
        return valid_symbols[:count]
    
    def validate_symbol(self, symbol: str) -> bool:
        """
        验证交易对是否有效
        
        Args:
            symbol: 交易对符号
            
        Returns:
            是否有效
        """
        try:
            # 尝试获取交易对信息
            symbol_info = self.client.get_symbol_info(symbol)
            return symbol_info is not None and symbol_info['status'] == 'TRADING'
        except:
            return False
    
    def get_symbol_info(self, symbol: str) -> dict:
        """
        获取交易对详细信息
        
        Args:
            symbol: 交易对符号
            
        Returns:
            交易对信息字典
        """
        try:
            symbol_info = self.client.get_symbol_info(symbol)
            if symbol_info:
                return {
                    'symbol': symbol_info['symbol'],
                    'baseAsset': symbol_info['baseAsset'],
                    'quoteAsset': symbol_info['quoteAsset'],
                    'status': symbol_info['status']
                }
        except Exception as e:
            logger.error(f"获取 {symbol} 信息失败: {e}")
        
        return {}
